package com.ruoyi.system.waishu;

import com.alibaba.fastjson.JSONObject;
import io.netty.channel.ChannelOption;
import io.netty.handler.timeout.ReadTimeoutException;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.WriteTimeoutHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.http.MediaType;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.http.client.reactive.ReactorResourceFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import reactor.netty.http.client.HttpClient;
import reactor.netty.resources.ConnectionProvider;
import reactor.netty.resources.LoopResources;

import java.time.Duration;
import java.util.Map;
import java.util.function.Function;

@Slf4j
@Component
@RefreshScope
@ConfigurationProperties(prefix = "httptask")
public class WebClientUtil {
    private static int timeOut ;
    private static int totalCount;//最大连接数
    private static int count;//工作连接数
    private static WebClient WEB_CLIENT ;
    private static final ReactorResourceFactory REACTOR_RESOURCE_FACTORY = new ReactorResourceFactory();

    static {
        REACTOR_RESOURCE_FACTORY.setUseGlobalResources(false);
        ConnectionProvider provider = ConnectionProvider.builder("httpClient")
            .maxConnections(totalCount)
            .maxIdleTime(Duration.ofSeconds(90))
            .maxLifeTime(Duration.ofSeconds(120))
            .lifo()
            .metrics(true)
            .pendingAcquireMaxCount(totalCount * 10)
            .pendingAcquireTimeout(Duration.ofSeconds(90))
            .build();
        REACTOR_RESOURCE_FACTORY.setConnectionProvider(provider);
        REACTOR_RESOURCE_FACTORY.setLoopResources(LoopResources.create("httpClient",count,true));
        Function<HttpClient, HttpClient>  mappper = client ->
              client.tcpConfiguration(c->
                  c.option(ChannelOption.CONNECT_TIMEOUT_MILLIS,timeOut*1000)
                      .option(ChannelOption.TCP_NODELAY,true)
                      .doOnConnected(conn->{
                          conn.addHandlerLast(new ReadTimeoutHandler(timeOut));
                          conn.addHandlerLast(new WriteTimeoutHandler(timeOut));
                      }));
        ReactorClientHttpConnector connector = new ReactorClientHttpConnector(REACTOR_RESOURCE_FACTORY, mappper);
        WEB_CLIENT = WebClient.builder()
            .filter(((request, next) -> {
                return next.exchange(request).retry(3);
            })).clientConnector(connector).build();
    }

    public static Mono<String> get(String url, Map<String,Object> map){
        if(map.size()>0){
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append(url);
            if(url.contains("?"))
                stringBuilder.append("&");
            else
                stringBuilder.append("?");
            map.keySet().forEach(key->{
                stringBuilder.append(key).append("=").append(map.get(key).toString()).append("&");
            });
            url = stringBuilder.toString();
        }
        return WEB_CLIENT.get().uri(url).retrieve().bodyToMono(String.class);
    }

    public static Mono<String> post(String url,Map<String,Object> params){
        MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
        if(params!=null && params.size()>0){
            map.setAll(params);
        }
        return WEB_CLIENT.post().uri(url)//.contentType(MediaType.APPLICATION_JSON)
            .bodyValue(map).retrieve().bodyToMono(String.class);
    }

    public static Mono<JSONObject> postApi(String url, ApiReq req){
        return WEB_CLIENT.post().uri(url).contentType(MediaType.APPLICATION_JSON)
            .bodyValue(req).retrieve().bodyToMono(JSONObject.class)
            .doOnError(ReadTimeoutException.class,err->{
                log.error("{}服务{}超时异常",req.getReqId(),req.getApiCode(),err);
            })
            .doOnError(Exception.class,err->{
                log.error("{}服务{}调用异常",req.getReqId(),req.getApiCode(),err);
            });
    }


}
